home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / octa209s.zip / octave-2.09 / libs / kpathsea / pathsearch.c < prev    next >
C/C++ Source or Header  |  1996-11-02  |  14KB  |  424 lines

  1. /* pathsearch.c: look up a filename in a path.
  2.  
  3. Copyright (C) 1993, 94 Karl Berry.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.  */
  18.  
  19. /* Modified by Klaus Gebhardt, 1996 */
  20.  
  21. #include <kpathsea/config.h>
  22.  
  23. #include <kpathsea/c-fopen.h>
  24. #include <kpathsea/absolute.h>
  25. #include <kpathsea/expand.h>
  26. #include <kpathsea/db.h>
  27. #include <kpathsea/pathsearch.h>
  28. #include <kpathsea/readable.h>
  29. #include <kpathsea/str-list.h>
  30. #include <kpathsea/str-llist.h>
  31. #include <kpathsea/variable.h>
  32.  
  33. #include <time.h> /* for `time' */
  34.  
  35. /* This function is called after every search to record the filename(s)
  36.    found in $TEXMFLOG or if debugging.  */
  37.  
  38. static void
  39. log_search P1C(str_list_type, filenames)
  40. {
  41.   static FILE *log_file = NULL;
  42.   static boolean first_time = true;
  43.   
  44.   if (first_time)
  45.     {
  46.       string log_name = kpse_var_expand ("$TEXMFLOG");
  47.       first_time = false;
  48.       /* Get name from either envvar or config file.  */
  49.       if (log_name && *log_name)
  50.         {
  51.           log_file = fopen (log_name, FOPEN_A_MODE);
  52.           if (!log_file)
  53.             perror (log_name);
  54.           free (log_name);
  55.         }
  56.     }
  57.  
  58.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file)
  59.     {
  60.       unsigned e;
  61.       
  62.       /* FILENAMES should never be null, but safety doesn't hurt.  */
  63.       for (e = 0;
  64.            e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e);
  65.            e++)
  66.         {
  67.           string filename = STR_LIST_ELT (filenames, e);
  68.           
  69.           /* Only record absolute filenames, for privacy.  */
  70.           if (log_file && kpse_absolute_p (filename, false))
  71. #ifdef __EMX__
  72.             fprintf (log_file, "%lu %s\n", time (NULL), filename);
  73. #else
  74.             fprintf (log_file, "%u %s\n", time (NULL), filename);
  75. #endif
  76.  
  77.           /* And show them online, if debugging.  We've already started
  78.              the debugging line in `search', where this is called, so
  79.              just print the filename here, don't use DEBUGF.  */
  80.           if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  81.             fputs (filename, stderr);
  82.         }
  83.     }
  84. }
  85.  
  86. /* Concatenate each element in DIRS with NAME (assume each ends with a
  87.    /, to save time).  If SEARCH_ALL is false, return the first readable
  88.    regular file.  Else continue to search for more.  In any case, if
  89.    none, return a list containing just NULL.
  90.  
  91.    We keep a single buffer for the potential filenames and reallocate
  92.    only when necessary.  I'm not sure it's noticeably faster, but it
  93.    does seem cleaner.  (We do waste a bit of space in the return
  94.    value, though, since we don't shrink it to the final size returned.)  */
  95.  
  96. #define INIT_ALLOC 75  /* Doesn't much matter what this number is.  */
  97.  
  98. static str_list_type
  99. dir_list_search P3C(str_llist_type *, dirs,  const_string, name,
  100.                     boolean, search_all)
  101. {
  102.   str_llist_elt_type *elt;
  103.   str_list_type ret;
  104.   unsigned name_len = strlen (name);
  105.   unsigned allocated = INIT_ALLOC;
  106.   string potential = xmalloc (allocated);
  107.  
  108.   ret = str_list_init ();
  109.   
  110.   for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt))
  111.     {
  112.       const_string dir = STR_LLIST (*elt);
  113.       unsigned dir_len = strlen (dir);
  114.       
  115.       while (dir_len + name_len + 1 > allocated)
  116.         {
  117.           allocated += allocated;
  118.           XRETALLOC (potential, allocated, char);
  119.         }
  120.       
  121.       strcpy (potential, dir);
  122.       strcat (potential, name);
  123.       
  124.       if (kpse_readable_file (potential))
  125.         { 
  126.           str_list_add (&ret, potential);
  127.           
  128.           /* Move this element towards the top of the list.  */
  129.           str_llist_float (dirs, elt);
  130.           
  131.           /* If caller only wanted one file returned, no need to
  132.              terminate the list with NULL; the caller knows to only look
  133.              at the first element.  */
  134.           if (!search_all)
  135.             return ret;
  136.  
  137.           /* Start new filename.  */
  138.           allocated = INIT_ALLOC;
  139.           potential = xmalloc (allocated);
  140.         }
  141.     }
  142.   
  143.   /* If we get here, either we didn't find any files, or we were finding
  144.      all the files.  But we're done with the last filename, anyway.  */
  145.   free (potential);
  146.   
  147.   return ret;
  148. }
  149.  
  150. /* This is called when NAME is absolute or explicitly relative; if it's
  151.    readable, return (a list containing) it; otherwise, return NULL.  */
  152.  
  153. static str_list_type
  154. absolute_search P1C(string, name)
  155. {
  156.   str_list_type ret_list;
  157.   string found = kpse_readable_file (name);
  158.   
  159.   /* Some old compilers can't initialize structs.  */
  160.   ret_list = str_list_init ();
  161.  
  162.   /* If NAME wasn't found, free the expansion.  */
  163.   if (name != found)
  164.     free (name);
  165.  
  166.   /* Add `found' to the return list even if it's null; that tells
  167.      the caller we didn't find anything.  */
  168.   str_list_add (&ret_list, found);
  169.   
  170.   return ret_list;
  171. }
  172.  
  173. /* If DB_DIR is a prefix of PATH_ELT, return true; otherwise false.
  174.    That is, the question is whether to try the db for a file looked up
  175.    in PATH_ELT.  If PATH_ELT == ".", for example, the answer is no. If
  176.    PATH_ELT == "/usr/local/lib/texmf/fonts//tfm", the answer is yes.
  177.    
  178.    In practice, ls-R is only needed for lengthy subdirectory
  179.    comparisons, but there's no gain to checking PATH_ELT to see if it is
  180.    a subdir match, since the only way to do that is to do a string
  181.    search in it, which is all we do anyway.
  182.    
  183.    In fact, we do a simple string compare, ignoring // complications,
  184.    since in practice I believe //'s will always be after `kpse_db_dir',
  185.    i.e., we would never want to find ls-R in /usr//texmf.  */
  186.    
  187. static boolean
  188. elt_in_db P1C(const_string, path_elt)
  189. {
  190.   boolean found = false;
  191.   
  192.   /* If `kpse_db_dir' is not set, we're being called from `read_files'
  193.      for the very first time -- for cnf file initialization.  We can't
  194.      use ls-R for that.  */
  195.   if (kpse_db_dir)
  196.     {
  197.       string db_temp = kpse_db_dir;
  198.  
  199.       while (!found && *db_temp++ == *path_elt++)
  200.         { /* If we've matched the entire db directory, it's good.  */
  201.           if (*db_temp == 0)
  202.             found = true;
  203.           /* If we've reached the end of PATH_ELT, but not the end of the db
  204.              directory, it's no good.  */
  205.           else if (*path_elt == 0)
  206.             break;
  207.         }
  208.     }
  209.  
  210.   return found;
  211. }
  212.  
  213.  
  214. /* This is the hard case -- look for NAME in PATH.  If
  215.    ALL is false, just return the first file found.  Otherwise,
  216.    search all elements of PATH.  */
  217.  
  218. static str_list_type
  219. path_search P4C(const_string, path,  string, name,
  220.                 boolean, must_exist,  boolean, all)
  221. {
  222.   string elt;
  223.   str_list_type ret_list;
  224.   boolean done = false;
  225.   ret_list = str_list_init (); /* some compilers lack struct initialization */
  226.  
  227.   for (elt = kpse_path_element (path); !done && elt;
  228.        elt = kpse_path_element (NULL))
  229.     {
  230.       boolean try_db;
  231.       boolean allow_disk_search = true;
  232.       str_list_type *found = NULL;
  233.       
  234.       if (*elt == '!' && *(elt + 1) == '!')
  235.         { /* Magic leading chars in a path element means don't search the
  236.              disk regardless.  And move past the magic to get to the name.  */
  237.           allow_disk_search = false;
  238.           elt += 2;
  239.         }
  240.       
  241.       /* Try the prebuilt db only if it's relevant to this path element. */
  242.       try_db = elt_in_db (elt);
  243.       found = try_db ? kpse_db_search (name, elt, all) : NULL;
  244.       
  245.       /* Search the filesystem if (1) the path spec allows it, and either
  246.          (2a) the db was irrelevant to ELT (try_db == false); or
  247.          (2b) no db exists (kpse_db_search returns NULL); or
  248.          (3) NAME was not in the db (kpse_db_search returns an empty list)
  249.              and MUST_EXIST.
  250.          In (2a) and (2b), `found' will be NULL.  */
  251.       if (allow_disk_search && (!found || (!STR_LIST (*found) && must_exist)))
  252.         {
  253.           str_llist_type *dirs = kpse_element_dirs (elt);
  254.           if (dirs && *dirs)
  255.             {
  256.               if (!found)
  257.                 found = XTALLOC1 (str_list_type);
  258.               *found = dir_list_search (dirs, name, all);
  259.             }
  260.         }
  261.  
  262.       /* Did we find anything anywhere?  */
  263.       if (found && STR_LIST (*found))
  264.         if (all)
  265.           str_list_concat (&ret_list, *found);
  266.         else
  267.           {
  268.             str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
  269.             done = true;
  270.           }
  271.       
  272.       /* Free the list space, if any (but not the elements).  */
  273.       if (found)
  274.     {
  275.       str_list_free (found);
  276.       free (found);
  277.     }
  278.     }
  279.  
  280.   /* Free the expanded name we were passed.  It can't be in the return
  281.      list, since the path directories got unconditionally prepended.  */
  282.   free (name);
  283.   
  284.   return ret_list;
  285. }      
  286.  
  287. /* Search PATH for ORIGINAL_NAME.  If ALL is false, or ORIGINAL_NAME is
  288.    absolute_p, check ORIGINAL_NAME itself.  Otherwise, look at each
  289.    element of PATH for the first readable ORIGINAL_NAME.
  290.    
  291.    Always return a list; if no files are found, the list will
  292.    contain just NULL.  If ALL is true, the list will be
  293.    terminated with NULL.  */
  294.  
  295. static string *
  296. search P4C(const_string, path,  const_string, original_name,
  297.            boolean, must_exist,  boolean, all)
  298. {
  299.   str_list_type ret_list;
  300.  
  301.   /* Make a leading ~ count as an absolute filename, and expand $FOO's.  */
  302.   string name = kpse_expand (original_name);
  303.   
  304.   /* If the first name is absolute or explicitly relative, no need to
  305.      consider PATH at all.  */
  306.   boolean absolute_p = kpse_absolute_p (name, true);
  307.   
  308.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  309.     DEBUGF4 ("search(file=%s, must_exist=%d, find_all=%d, path=%s).\n",
  310.              name, must_exist, all, path);
  311.  
  312.   /* Find the file(s). */
  313.   ret_list = absolute_p ? absolute_search (name)
  314.                         : path_search (path, name, must_exist, all);
  315.   
  316.   /* Append NULL terminator if we didn't find anything at all, or we're
  317.      supposed to find ALL and the list doesn't end in NULL now.  */
  318.   if (STR_LIST_LENGTH (ret_list) == 0
  319.       || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
  320.     str_list_add (&ret_list, NULL);
  321.  
  322.   /* Record the filenames we found, if desired.  And wrap them in a
  323.      debugging line if we're doing that.  */
  324.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  325.     DEBUGF1 ("search(%s) =>", original_name);
  326.   log_search (ret_list);
  327.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  328.     putc ('\n', stderr);
  329.   
  330.   return STR_LIST (ret_list);
  331. }
  332.  
  333. /* Search PATH for the first NAME.  */
  334.  
  335. string
  336. kpse_path_search P3C(const_string, path,  const_string, name,
  337.                      boolean, must_exist)
  338. {
  339.   static string *ret_list = 0;
  340.  
  341.   if (ret_list)
  342.     {
  343.       free (ret_list);
  344.       ret_list = 0;  /* Don't let an interrupt in search() cause trouble */
  345.     }
  346.  
  347.   ret_list = search (path, name, must_exist, false);
  348.  
  349.   return *ret_list;  /* Freeing this is caller's responsibility */
  350. }
  351.  
  352.  
  353. /* Search all elements of PATH for files named NAME.  Not sure if it's
  354.    right to assert `must_exist' here, but it suffices now.  */
  355.  
  356. string *
  357. kpse_all_path_search P2C(const_string, path,  const_string, name)
  358. {
  359.   string *ret = search (path, name, true, true);
  360.   return ret;
  361. }
  362.  
  363. #ifdef TEST
  364.  
  365. void
  366. test_path_search (const_string path, const_string file)
  367. {
  368.   string answer;
  369.   string *answer_list;
  370.   
  371.   printf ("\nSearch %s for %s:\t", path, file);
  372.   answer = kpse_path_search (path, file);
  373.   puts (answer ? answer : "(null)");
  374.  
  375.   printf ("Search %s for all %s:\t", path, file);
  376.   answer_list = kpse_all_path_search (path, file);
  377.   putchar ('\n');
  378.   while (*answer_list)
  379.     {
  380.       putchar ('\t');
  381.       puts (*answer_list);
  382.       answer_list++;
  383.     }
  384. }
  385.  
  386. #define TEXFONTS "/usr/local/lib/tex/fonts"
  387.  
  388. int
  389. main ()
  390. {
  391.   /* All lists end with NULL.  */
  392.   test_path_search (".", "nonexistent");
  393.   test_path_search (".", "/nonexistent");
  394.   test_path_search ("/k:.", "kpathsea.texi");
  395.   test_path_search ("/k:.", "/etc/fstab");
  396.   test_path_search (".:" TEXFONTS "//", "cmr10.tfm");
  397.   test_path_search (".:" TEXFONTS "//", "logo10.tfm");
  398.   test_path_search (TEXFONTS "//times:.::", "ptmr.vf");
  399.   test_path_search (TEXFONTS "/adobe//:"
  400.                     "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa");
  401.   
  402.   test_path_search ("~karl", ".bashrc");
  403.   test_path_search ("/k", "~karl/.bashrc");
  404.  
  405.   xputenv ("NONEXIST", "nonexistent");
  406.   test_path_search (".", "$NONEXISTENT");
  407.   xputenv ("KPATHSEA", "kpathsea");
  408.   test_path_search ("/k:.", "$KPATHSEA.texi");  
  409.   test_path_search ("/k:.", "${KPATHSEA}.texi");  
  410.   test_path_search ("$KPATHSEA:.", "README");  
  411.   test_path_search (".:$KPATHSEA", "README");  
  412.   
  413.   return 0;
  414. }
  415.  
  416. #endif /* TEST */
  417.  
  418.  
  419. /*
  420. Local variables:
  421. test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a"
  422. End:
  423. */
  424.